{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fbe3c323-7f07-40b2-92ba-5a1ac67614d2",
   "metadata": {},
   "source": [
    "# Pickling of NGSolve objects\n",
    "\n",
    "Python objects an be converted to byte-streams, which can be stored to files, and later the \n",
    "Python object can be reconstructed by the inverse process. In general this is known as serialization,\n",
    "in Python it is called pickling, see\n",
    "https://docs.python.org/3/library/pickle.html.\n",
    "Another usecase is parallel computing, where it is used to send whole Python objects across a network.\n",
    "\n",
    "Many of the NGSolve-objects support pickling."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36756d25-4a09-42a7-9d55-e67eb3a01ed1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ngsolve import *\n",
    "from ngsolve.webgui import Draw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd0d7404-a678-4511-8024-d605741c258d",
   "metadata": {},
   "outputs": [],
   "source": [
    "mesh = Mesh(unit_square.GenerateMesh(maxh=0.3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dad5296b-5233-4d85-a615-fae0fdf4b55e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pickle\n",
    "outfile = open(\"mesh.pkl\", \"wb\")\n",
    "pickle.dump(mesh, outfile)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2faa34d7-9ac9-489a-9742-8684c6583dcf",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"mesh.pkl\", \"rb\")\n",
    "mesh2 = pickle.load(infile)\n",
    "mesh2.nv, mesh2.ne"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cc803b6-387c-44f0-b604-40f72ad5f20b",
   "metadata": {},
   "outputs": [],
   "source": [
    "Draw (mesh2);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "317411d4-aa7d-4071-949d-174ca0b24258",
   "metadata": {},
   "source": [
    "## Shared objects remain shared\n",
    "\n",
    "When we create several spaces on the same mesh, all spaces link to the mesh via a shared pointer. Similarly, if we have several `GridFunction`s defined on the same space, they link to it using a shared pointer. These shared objects remain shared after pickling and unpickling:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c67d391f-fdb7-40af-84f6-72ba8fcf47b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "fes = H1(mesh, order=2)\n",
    "gfu1 = GridFunction(fes)\n",
    "gfu2 = GridFunction(fes)\n",
    "gfu1.Set(x)\n",
    "gfu2.Set(y)\n",
    "\n",
    "outfile = open(\"gridfunction.pkl\", \"wb\")\n",
    "pickle.dump([gfu1,gfu2], outfile)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e0aa799-486a-410c-b0b9-7a1f3cb45771",
   "metadata": {},
   "outputs": [],
   "source": [
    "infile = open(\"gridfunction.pkl\", \"rb\")\n",
    "gfv1,gfv2 = pickle.load(infile)\n",
    "print (\"the same spaces:\", id(gfv1.space), \"=?=\", id(gfv2.space))\n",
    "\n",
    "Draw (gfv1);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e98b602a-e488-4d0b-96b6-57437675f5ed",
   "metadata": {},
   "source": [
    "## Pickling expression trees\n",
    "\n",
    "`CoefficientFunction` expression trees support pickling as well."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b580e188-1e73-4192-a550-50090af73939",
   "metadata": {},
   "outputs": [],
   "source": [
    "func = x*gfu1 + y\n",
    "print (func)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30002833-9218-4b67-a418-4b121b5ad979",
   "metadata": {},
   "outputs": [],
   "source": [
    "outfile = open(\"func.pkl\", \"wb\")\n",
    "pickle.dump([mesh,func], outfile)\n",
    "\n",
    "infile = open(\"func.pkl\", \"rb\")\n",
    "mesh2,func2 = pickle.load(infile)\n",
    "\n",
    "print (func2)\n",
    "\n",
    "Draw (func2, mesh2);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42fed9f8-3e03-437f-b725-50b6206a5671",
   "metadata": {},
   "source": [
    "We were pickling the mesh explicitly to have it available for drawing, it would be contained in the function `func` anyway.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67639c86",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.remove(\"mesh.pkl\")\n",
    "os.remove(\"gridfunction.pkl\")\n",
    "os.remove(\"func.pkl\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "220073b3-4cd1-4edd-af9f-2f2e36064196",
   "metadata": {},
   "source": [
    "## Implementation of pickling\n",
    "\n",
    "[pybind11-pickling](https://pybind11.readthedocs.io/en/stable/advanced/classes.html?highlight=pickling#pickling-support) supports wrapping of serialization of user-classes. \n",
    "\n",
    "And then there is ngcore - archive ..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c74d1af4-678d-442b-a9ee-150af772e57b",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "de92cebb",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
